﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;

namespace NetFrameworkExtensions.ComponentModel
{
  public delegate void PropertyChainChangedEventHandler(object sender, PropertyChainChangedEventArgs e);

  public class PropertyChainChangedEventArgs : EventArgs
  {
    public PropertyChainChangedEventArgs(PropertyChainStep originalStep, IEnumerable<PropertyChainStep> steps)
    {
      if (steps == null)
        throw new ArgumentNullException("steps");

      this.OriginalStep = originalStep;
      this.Steps = steps;
    }

    public bool IsMatch(Type senderType, string propertyName)
    {
      return OriginalStep != null && OriginalStep.IsMatch(senderType, propertyName) ||
        Steps.Any(s => s.IsMatch(senderType, propertyName));
    }

    public bool IsMatch<TSender>(Expression<Func<TSender, object>> property)
    {
      var propertyName = ((PropertyInfo)((MemberExpression)property.Body).Member).Name;
      return IsMatch(typeof(TSender), propertyName);
    }

    public bool IsMatch<TSender>(string propertyName)
    {
      return IsMatch(typeof(TSender), propertyName);
    }

    public PropertyChainStep OriginalStep { get; set; }

    public IEnumerable<PropertyChainStep> Steps { get; set; }
  }

  public class PropertyChainStep
  {
    public PropertyChainStep(object sender, PropertyChangedEventArgs propertyArgs)
    {
      this.Sender = sender;
      this.PropertyArgs = propertyArgs;
    }

    public object Sender { get; private set; }
    public PropertyChangedEventArgs PropertyArgs { get; private set; }

    public bool IsMatch(Type senderType, string propertyName)
    {
      if (senderType != null)
        if (Sender == null || !senderType.IsAssignableFrom(Sender.GetType()))
          return false;

      if (!string.IsNullOrEmpty(propertyName))
        if (PropertyArgs == null || propertyName != PropertyArgs.PropertyName)
          return false;

      return true;
    }

    public bool IsMatch<TSender>(Expression<Func<TSender, object>> property)
    {
      var propertyName = ((PropertyInfo)((MemberExpression)property.Body).Member).Name;
      return IsMatch(typeof(TSender), propertyName);
    }

    public bool IsMatch<TSender>(string propertyName)
    {
      return IsMatch(typeof(TSender), propertyName);
    }
  }
}
